<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>数据库文档导出助手</title>
    <script src="/js/vue.js"></script>
    <link rel="icon" type="image/x-icon" href="/icon/favicon.ico">
    <!-- 引入样式 -->
    <link rel="stylesheet" href="/css/element.css">
    <link rel="stylesheet" href="/css/normalize.css">
    <!-- 引入组件库 -->
    <script src="/js/element.js"></script>
    <script src="/js/axios.min.js"></script>
    <link rel="stylesheet" href="/css/document.css">
</head>
<body>
<div id="app">
    <div class="document-layout">
        <div class="container">
            <div class="nav-bar">
                <el-page-header @back="goBack" class="title" content="数据库文档导出助手"></el-page-header>
                <div class="button-bar">
                    <el-button @click="formatDialogVisible = true" type="text">输出格式</el-button>
                    <el-button @click="fieldDialogVisible = true" type="text">列名编辑</el-button>
                    <el-button @click="preview" type="text">预览文档</el-button>
                    <el-dropdown class="unselectable" @command="download" trigger="click">
                        <span class="el-dropdown-link">下载文档
                            <i class="el-icon-arrow-down el-icon--right"></i>
                        </span>
                        <el-dropdown-menu slot="dropdown">
                            <el-dropdown-item command="word">
                                <img src="/icon/svg/word.svg" width="20" height="20" alt=""/>
                                <span>Word</span>
                            </el-dropdown-item>
                            <el-dropdown-item command="pdf">
                                <img alt="" src="/icon/svg/pdf.svg" width="20" height="20">
                                <span>PDF</span>
                            </el-dropdown-item>
                            <el-dropdown-item command="markdown">
                                <img src="/icon/svg/markdown.svg" width="20" height="20" alt="">
                                <span>Markdown</span>
                            </el-dropdown-item>
                            <el-dropdown-item command="html">
                                <img src="/icon/svg/html.svg" width="20" height="20" alt="">
                                <span>HTML</span>
                            </el-dropdown-item>
                        </el-dropdown-menu>
                    </el-dropdown>
                </div>
            </div>
            <div class="aside">
                <el-tree
                        ref="structureTree"
                        :load="loadNode"
                        @check-change="selectNode"
                        lazy
                        show-checkbox
                        node-key="nodeId"
                        :props="defaultProps">
                    <template v-slot="{ node, data }">
                        <div class="tree-node">
                            <img v-if="data.schemaNode" src="/icon/svg/database.svg" width="20" height="20" alt="">
                            <img v-if="!data.schemaNode" src="/icon/svg/database-table.svg" width="20" height="20"
                                 alt="">
                            <span>{{ node.label }}</span>
                        </div>
                    </template>
                </el-tree>
            </div>
            <div class="drag-bar"></div>
            <div class="content"
                 v-loading="loading"
                 :element-loading-text="loadingMessage"
                 element-loading-spinner="el-icon-loading"
                 element-loading-background="rgba(0, 0, 0, 0.8)">
                <iframe id="iframe" :src="pdf"></iframe>
            </div>
        </div>
    </div>

    <el-dialog :lock-scroll="false"
               title="列名编辑"
               @close="resetColumns"
               :visible.sync="fieldDialogVisible"
               width="660px">
        <el-checkbox-group class="checkbox-group" v-model="tempDefaultColumns">
            <div :key="index" v-for="(item,index) of tempColumns" class="checkbox-item">
                <el-checkbox class="checkbox" :label="item.value">
                    <el-input size="small" v-model="item.label" :placeholder="placeholders[index]"></el-input>
                </el-checkbox>
                <el-input-number size="small" v-model="item.order"></el-input-number>
            </div>
        </el-checkbox-group>
        <span slot="footer" class="dialog-footer">
             <el-button size="small" type="info" @click="resetDefault">恢复默认</el-button>
            <el-button size="small" @click="fieldDialogVisible = false">取 消</el-button>
            <el-button size="small" type="primary" @click="changeColumns">确 定</el-button>
        </span>
    </el-dialog>

    <el-dialog width="850px"
               :lock-scroll="false"
               title="输出格式"
               :visible.sync="formatDialogVisible">
        <el-form label-width="80px" :model="formatStyle" :inline="true">
            <el-form-item label="数据库名称格式" label-width='120px'>
                <el-input v-model="formatStyle.database.format" size="small"></el-input>
            </el-form-item>
            <el-form-item label="字体大小" label-width='120px'>
                <el-slider v-model="formatStyle.database.fontSize" size="small"></el-slider>
            </el-form-item>
            <el-form-item label="字体样式" label-width='120px'>
                <el-checkbox v-model="formatStyle.database.bold">加粗</el-checkbox>
            </el-form-item>
            <el-form-item label="" label-width='120px'>
            </el-form-item>
            <el-form-item label="表名称格式" label-width='120px'>
                <el-input v-model="formatStyle.table.format" size="small"></el-input>
            </el-form-item>
            <el-form-item label="字体大小" label-width='120px'>
                <el-slider :min="10" :max="25" v-model="formatStyle.table.fontSize" size="small"></el-slider>
            </el-form-item>
            <el-form-item label="字体样式" label-width='120px'>
                <el-checkbox v-model="formatStyle.table.bold">加粗</el-checkbox>
            </el-form-item>
            <el-form-item label="" label-width='120px'>
            </el-form-item>
            <el-form-item label="文件名称格式" label-width='120px'>
                <el-input v-model="formatStyle.fileName" size="small"></el-input>
            </el-form-item>
        </el-form>
        <span slot="footer" class="dialog-footer">
            <el-button size="small" @click="formatDialogVisible = false">取 消</el-button>
            <el-button size="small" type="primary" @click="saveFormatStyle">确 定</el-button>
        </span>
    </el-dialog>
</div>
<script>
    // 创建一个新的 Vue 实例
    new Vue({
        el: '#app',
        data: {
            data: [],
            loading: false,
            loadingMessage: '',
            fieldDialogVisible: false,
            formatDialogVisible: false,
            allColumns: [],
            tempColumns: [],
            placeholders: [],
            defaultColumns: [],
            tempDefaultColumns: [],
            defaultProps: {
                children: 'children',
                label: 'labelName'
            },
            formatStyle: {
                database: {
                    format: '数据库名称 ${databaseName}',
                    fontSize: 15,
                    bold: true
                },
                table: {
                    format: '${tableName} ${description}',
                    fontSize: 15,
                    bold: false
                },
                fileName: '数据库文档_yyyy-MM-dd'
            },
            pdf: ''
        },
        methods: {
            goBack() {
                window.location.href = '/'
            },
            resetDefault() {
                this.getTableColumns();
            },
            resetColumns() {
                this.tempColumns = JSON.parse(JSON.stringify(this.allColumns));
                this.tempDefaultColumns = [...this.defaultColumns];
            },
            changeColumns() {
                this.allColumns = this.tempColumns;
                this.defaultColumns = this.tempDefaultColumns;
                this.saveColumnConfig()
                this.fieldDialogVisible = false;
            },
            async selectNode(data, selected) {
                let structureTree = this.$refs.structureTree;
                let node = structureTree.getNode(data)
                if (selected && data.childrenCount !== 0 && node.level === 1) {
                    let tables = await this.getTables(data)
                    // 新增子节点
                    structureTree.updateKeyChildren(data.nodeId, tables)
                    this.$nextTick(() => {
                        for (let table of tables) {
                            structureTree.setChecked(table, true)
                            structureTree.getNode(table).isLeaf = true
                        }
                    })
                } else if (!selected && node.level === 2) {
                    structureTree.setChecked(data, false)
                } else if (!selected && node.level === 1 && !structureTree.getHalfCheckedKeys().includes(data.nodeId)) {
                    structureTree.setChecked(data, false)
                    for (let childNode of node.childNodes) {
                        structureTree.setChecked(childNode.data, false)
                    }
                }
            },
            async loadNode(node, resolve) {
                try {
                    this.loading = true;
                    if (node.level === 0) {
                        let databases = await this.getDatabases();
                        resolve(databases);
                        this.$nextTick(() => {
                            let structureTree = this.$refs.structureTree;
                            for (let database of databases) {
                                database.childrenCount === 0 && (structureTree.getNode(database).isLeaf = true)
                            }
                        })
                    } else if (node.level === 1) {
                        let tables = await this.getTables(node.data);
                        resolve(tables);
                        this.$nextTick(() => {
                            let structureTree = this.$refs.structureTree;
                            for (let table of tables) {
                                structureTree.getNode(table).isLeaf = true
                            }
                        })
                    }
                } catch (err) {
                    this.showMessage('error', err)
                    resolve([])
                } finally {
                    this.loading = false;
                }
            },
            getDatabases() {
                return new Promise((resolve, reject) => {
                    axios.get('/document/getDatabases').then(res => {
                        if (res.data.code === 200) {
                            resolve(res.data.data);
                        } else {
                            reject("数据加载失败");
                        }
                    }).catch(err => {
                        reject(err);
                    });
                });
            },
            getTables(data) {
                let schemaName = data.labelName;
                let parentNodeId = data.nodeId;
                return new Promise((resolve, reject) => {
                    axios.get('/document/getTables', {params: {parentNodeId, schemaName}}).then(res => {
                        if (res.data.code === 200) {
                            resolve(res.data.data);
                        } else {
                            reject("数据加载失败");
                        }
                    }).catch(err => {
                        reject(err);
                    });
                });
            },
            loadColumnConfig() {
                const configStr = localStorage.getItem("column-config");
                if (configStr) {
                    const config = JSON.parse(configStr);
                    this.allColumns = config.allColumns;
                    this.tempColumns = config.allColumns;
                    this.defaultColumns = config.defaultColumns;
                    this.tempDefaultColumns = config.defaultColumns;
                    this.placeholders = this.allColumns.map(item => item.label);
                } else {
                    this.getTableColumns();
                }
            },
            getTableColumns() {
                axios.get('/document/getTableColumnInfo').then(res => {
                    if (res.data.code === 200) {
                        this.allColumns = res.data.data.allColumns;
                        this.tempColumns = JSON.parse(JSON.stringify(this.allColumns));
                        this.placeholders = this.allColumns.map(item => item.label);
                        this.defaultColumns = res.data.data.defaultColumns;
                        this.tempDefaultColumns = [...this.defaultColumns];
                        this.saveColumnConfig()
                    } else {
                        this.showMessage("error", "数据加载失败")
                    }
                }).catch(err => {
                    this.showMessage("error", "数据加载失败")
                })
            },
            saveColumnConfig() {
                let config = JSON.stringify({allColumns: this.allColumns, defaultColumns: this.defaultColumns});
                localStorage.setItem("column-config", config);
            },
            preview() {
                if (this.getCheckedNodes().length === 0) {
                    this.showMessage("error", "当前未选中任何数据表")
                    return;
                }
                this.loadingMessage = '正在拼命加载中...'
                this.loading = true
                axios({
                    method: 'post',
                    url: '/document/pdf/preview',
                    responseType: 'arraybuffer', // 指定响应类型为二进制流
                    data: {
                        dataList: this.getCheckedNodes(),
                        defaultColumns: this.allColumns.filter(item => this.defaultColumns.includes(item.value)),
                        formatStyle: this.formatStyle
                    }
                }).then(res => {
                    if (res.status === 200) {
                        const blob = new Blob([res.data], {type: "application/pdf"});
                        this.pdf = URL.createObjectURL(blob);
                    } else {
                        this.showMessage("error", "预览失败")
                    }
                    this.loading = false
                }).catch(err => {
                    this.showMessage("error", err.message)
                    this.loading = false
                });
            },
            download(type) {
                if (this.getCheckedNodes().length === 0) {
                    this.showMessage("error", "当前未选中任何数据表")
                    return;
                }
                this.loadingMessage = '正在下载中...'
                this.loading = true
                axios({
                    method: 'post',
                    url: `/document/${type}/download`,
                    responseType: 'blob', // 指定响应类型为二进制流
                    data: {
                        dataList: this.getCheckedNodes(),
                        contentType: this.contentType,
                        defaultColumns: this.allColumns.filter(item => this.defaultColumns.includes(item.value)),
                        formatStyle: this.formatStyle
                    }
                }).then(res => {
                    if (res.status === 200) {
                        let blob = new Blob([res.data])
                        let url = window.URL.createObjectURL(blob)
                        let downloadElement = document.createElement('a')
                        downloadElement.style.display = 'none'
                        downloadElement.href = url
                        downloadElement.setAttribute('download', decodeURIComponent(res.headers['filename']))
                        document.body.appendChild(downloadElement)
                        downloadElement.click()
                        document.body.removeChild(downloadElement) // 下载完成移除元素
                        window.URL.revokeObjectURL(url) // 释放掉blob对象
                        this.showMessage("success", "下载成功")
                    } else {
                        this.showMessage("error", "下载失败")
                    }
                    this.loading = false
                }).catch(err => {
                    this.showMessage("error", err.message)
                    this.loading = false
                });
            },
            getCheckedNodes() {
                let tree = this.$refs.structureTree;
                let checkedNodes = tree.getCheckedNodes();
                let parentNodeIds = new Set(checkedNodes.filter(node => node.parentNodeId != null).map(node => node.parentNodeId));
                for (let parentNodeId of parentNodeIds) {
                    let node = tree.getNode(parentNodeId).data;
                    if (!checkedNodes.includes(node)) {
                        checkedNodes.push(node)
                    }
                }
                return checkedNodes;
            },
            showMessage(type, message) {
                this.$message({
                    message: message,
                    type: type,
                });
            },
            saveFormatStyle() {
                let formatStyleJson = JSON.stringify(this.formatStyle);
                localStorage.setItem('DbSnap:formatStyle', formatStyleJson)
                this.formatDialogVisible = false
            },
            initFormatStyle() {
                const formatStyleJson = localStorage.getItem('DbSnap:formatStyle');
                if (formatStyleJson) {
                    this.formatStyle = JSON.parse(formatStyleJson)
                }
            },
            enableDragBar() {
                const container = document.querySelector('.container');
                const dragBar = document.querySelector('.drag-bar');
                const aside = document.querySelector('.aside');
                const content = document.querySelector('.content');
                const iframe = document.querySelector('iframe');
                const contentMinWidth = Number.parseInt(window.getComputedStyle(content).minWidth.replace('px', ''));
                const asideMinWidth = Number.parseInt(window.getComputedStyle(aside).minWidth.replace('px', ''));
                const dragBarWidth = dragBar.clientWidth;
                // 鼠标按下
                dragBar.onmousedown = function (e) {
                    // 鼠标开始的X轴坐标
                    let startX = e.clientX;
                    // 拖动块距离屏幕左侧的偏移量
                    let offsetLeft = dragBar.offsetLeft;
                    // 在拖拽期间禁用 iframe 的交互
                    iframe.style.pointerEvents = 'none';
                    // 鼠标移动
                    document.onmousemove = function (e) {
                        // 拖动块的最大移动距离
                        let maxWidth = container.clientWidth - contentMinWidth;
                        // 鼠标箭头移动位置
                        let pointerOffset = offsetLeft + (e.clientX - startX);

                        // 边界值处理
                        pointerOffset < asideMinWidth && (pointerOffset = asideMinWidth);
                        pointerOffset > maxWidth && (pointerOffset = maxWidth);

                        // 设置值（保证拖动块一直在拖动盒子的相对位置）
                        aside.style.width = pointerOffset + "px";
                        dragBar.style.left = (pointerOffset === maxWidth)
                            ? pointerOffset + 'px'
                            : (pointerOffset - dragBarWidth / 2) + "px";
                        content.style.left = pointerOffset + "px";
                        content.style.width = (container.clientWidth - pointerOffset) + 'px';
                        return false;
                    };

                    // 鼠标松开
                    document.onmouseup = function (e) {
                        document.onmousemove = null;
                        document.onmouseup = null;
                        dragBar.releaseCapture && dragBar.releaseCapture();
                        // 拖拽结束后恢复 iframe 的交互
                        iframe.style.pointerEvents = 'auto';
                    };
                    dragBar.setCapture && dragBar.setCapture();
                    return false;
                };
            }
        },
        mounted: function () {
            this.loadColumnConfig();
            this.initFormatStyle();
            this.enableDragBar();
        }
    });
</script>
</body>
</html>